package org.mathcloud.math.io.utility.generic;

import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.DOMBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.*;
import java.util.List;


public class GenericXMLUtility
{
  public static String xml2String(Element el, String charsetName)
  {
  	if (el!=null)
    {
      Format format = Format.getPrettyFormat();
      if (charsetName!=null)
        format.setEncoding(charsetName);
      else format.setEncoding(DEFAULT_CHARSET);
      format.setExpandEmptyElements(false);
      XMLOutputter xmlOutputter = new XMLOutputter(format);
      return xmlOutputter.outputString(el);
    }
    else
    	return null;
  }

  public static Element string2XML(String in, String charsetName)
  {
  	if (in!=null)
    {
      String encoding = (charsetName !=null) ? charsetName : DEFAULT_CHARSET;
      org.w3c.dom.Document tempObj = parseXML(in,encoding);
      if (tempObj!=null)
      {
        DOMBuilder builder = new DOMBuilder();
        Document doc = builder.build(parseXML(in,encoding));
        Element res = doc.getRootElement();
        res.detach();
        return res;
      } else return null;
    } else return null;
  }

  public static File xml2File(Element xml, String fileName, String charsetName) 
  	throws FileNotFoundException, IOException
  {
  	XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
  	File file = new File(fileName);
  	Document doc = new Document();
  	doc.setRootElement(xml);
  	xmlOutputter.output(doc, new FileOutputStream(file));
  	return file;
  }
  
  public static org.w3c.dom.Document parseXML(String xml, String charsetName)
  {
    try
    {
      DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
      domFactory.setNamespaceAware(true); // never forget this
      DocumentBuilder builder = null;
      builder = domFactory.newDocumentBuilder();
      String encoding = (charsetName !=null) ? charsetName : DEFAULT_CHARSET;
      return  builder.parse(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes(encoding))));
    } catch (ParserConfigurationException e)
    {
    	log.error(e);
      return null;
    } catch (SAXException e)
    {
    	log.error(e);
      return null;
    } catch (UnsupportedEncodingException e)
    {
    	log.error(e);
      return null;
    } catch (IOException e)
    {
    	log.error(e);
      return null;
    }
  }

  public static void validateXML(String in, String xsdFile, String charsetName)
    throws SAXException, IOException
  {
    SchemaFactory factory = SchemaFactory.newInstance(XML_SCHEMA);
    File schemaLocation = new File(xsdFile);
    String encoding = (charsetName !=null) ? charsetName : DEFAULT_CHARSET;
    Schema schema = factory.newSchema(schemaLocation);
    Validator validator = schema.newValidator();
    Source source = new StreamSource(new BufferedInputStream(new ByteArrayInputStream(in
        .getBytes(encoding))));
    validator.validate(source);
  }
  
  @SuppressWarnings("rawtypes")
	public static boolean contains(Element arg0, String arg1, Namespace arg2) throws JDOMException
  {
		XPath xpath = XPath.newInstance(arg1);
		if (arg2!=null)
			xpath.addNamespace(arg2);
		else xpath.addNamespace("mml", "http://www.w3.org/1998/Math/MathML");
		List nodes = xpath.selectNodes(arg0);
		
		if (nodes!=null)
		{
			if (nodes.size()==1)
			{
				if (nodes.get(0) instanceof Boolean)
					return false;				
			}
			return (nodes.size()>0);
		}			
		else return false;		
  }
  
  @SuppressWarnings("rawtypes")
	public static List findElements(Element arg0, String arg1) throws JDOMException
  {
		XPath xpath = XPath.newInstance(arg1);
		xpath.addNamespace("mml", "http://www.w3.org/1998/Math/MathML");
		List nodes = xpath.selectNodes(arg0);
		return nodes;
  }
  
  public static final String DEFAULT_CHARSET = "UTF-8";
  private static final String XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
	public static final Logger log = Logger.getLogger(GenericXMLUtility.class);
}
